/*
 * This class provides a simple symmetric polynomial implementation
 */
#ifndef _SYMMETRICPOLYNOMIAL_H
#define _SYMMETRICPOLYNOMIAL_H

#include<map>  //A map is a good structure to implement symmetric polynomial
#include<vector>  //To implement the degree list
#include<string>
#include<iostream>
#include<algorithm>
#include "fraction.h"


//Traits for symmetric polynomial reading
template<typename T>
class symmetricpolynomial_traits{
      public:
             static std::istream& read(
                    std::istream& is,
                    T& sp);  //This gets a symmetricpolynomial type when called
};

template<typename T, 
         typename varsType = std::vector<std::string>, 
         typename degreeType = std::vector<int>,
         typename degreeStructureType = std::map<degreeType,T>,
         typename traits = symmetricpolynomial_traits<T> >
class symmetricpolynomial{
      public:
             //Constructors
             //symmetricpolynomial();
             symmetricpolynomial(varsType varList) : vars(varList){}
             //symmetricpolynomial(degreeType deg, T coeff); //Makes no sense
             symmetricpolynomial(degreeType deg, varsType variables, T coeff);
             symmetricpolynomial(degreeStructureType symmdeg, varsType variables);
             //Copy constructor
             symmetricpolynomial(const symmetricpolynomial& right) :
                                       symmDegree(right.symmDegree),
                                       vars(right.vars){}
             
             //symmetrically add two symmetric polynomials
             symmetricpolynomial add(symmetricpolynomial& right);
             symmetricpolynomial multiply(symmetricpolynomial& right);
             symmetricpolynomial multiply_constant(T& right);  //Multiply a constant to this
             int degree();  //Get this polynomial degree
             T lcoeff();  //Get the leading coefficient
             degreeType ldegree();  //Get the degree of the leading coefficient
             bool is_empty();
             varsType get_vars();
             
             //Operator overloading
             symmetricpolynomial operator+(symmetricpolynomial right);
             symmetricpolynomial operator-(symmetricpolynomial& right);
             symmetricpolynomial operator*(symmetricpolynomial right);
             symmetricpolynomial& operator=(symmetricpolynomial& right);
             
             bool operator==(symmetricpolynomial& right);
             bool operator!=(symmetricpolynomial& right);
             
             template<typename R, typename V, typename D, typename DS, typename Traits>
             friend std::istream& operator>>(std::istream& is, symmetricpolynomial<R,V,D,DS,Traits>& sp);
             
             template<typename R, typename V, typename D, typename DS, typename Traits>
             friend std::ostream& operator<<(std::ostream& os, symmetricpolynomial<R,V,D,DS,Traits> sp);
             
             template<typename R>
             friend std::istream& symmetricpolynomial_traits<R>::read(
                                  std::istream& is,
                                  R& sp);
                          //Handling symmetricpolynomial exceptions
             class symmetricpolynomial_exception: public std::exception{
                 virtual const char* what() const throw(){
                     return "Error in symmetric polynomial.";
                 }
             };
             
      private:
              degreeStructureType symmDegree;
              varsType vars;
              
              void clean_excedentary();
              void add_mon(degreeType deg, T coeff);
              bool same_list(degreeType deg1, degreeType deg2);
              void add_set(degreeStructureType dst);
              bool compare_vars(varsType vrs1, varsType vrs2);
              int count_terms(degreeType deg, int numb_vars);
              degreeStructureType multiply_constant(degreeStructureType& dst, T cnst);
              degreeStructureType multiply_constant(std::map<degreeType,fraction<int> >& dst, T cnst);
              std::map<degreeType,fraction<int> > multiply_monomial(degreeType deg1, degreeType deg2);
              degreeType sum_degree(degreeType deg1, degreeType deg2);
              
              
              std::istream& read(std::istream& is);  //Equivalence to toString
              std::ostream& write(std::ostream& os);
              //Some write auxiliary functions

              //std::string to_string();
              std::ostream& get_var_deg(std::ostream& os,varsType& vrs,degreeType deg);
              std::ostream& get_symm_mon(std::ostream& os,degreeType& deg, T& coeff);
              bool is_const_monomial(degreeType deg);
              
};
#endif
